<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>REST Files: GitHub-like file access with 1 page jQuery + 1 C# REST Service
    </title>
    <link rel="stylesheet" type="text/css" href="Content/Css/default.css" />
    <script type="text/javascript" src="Scripts/jquery-1.8.3.min.js"></script>
    <script type="text/javascript" src="Scripts/ajaxfileupload.js"></script>
</head>
<body>
    <a href="http://mono.servicestack.net" style="display: block; position: absolute;
        top: 5px; left: 10px;">
        <img src="http://mono.servicestack.net/icon-home.jpg" alt="ServiceStack Home" /></a>
    <div id="header">
        <div id="header-links">
            <a href="../ServiceStack.Hello/">Hello World</a> <a href="../Backbone.Todos/">Todos</a>
            <a href="../RedisStackOverflow/">Redis StackOverflow</a> <a href="../RestFiles/">REST
                Files</a> <a href="../ServiceStack.MovieRest/">REST Movies</a> <a href="../ServiceStack.Northwind/">
                    Northwind Database</a> <a href="../ServiceStack.Examples.Clients/">Ajax Client</a>
        </div>
        <a id="github" href="https://github.com/ServiceStack/ServiceStack.Examples">
            <img src="Content/Images/btn-github.png" alt="ServiceStack.Examples project" /></a>
    </div>
    <div id="title">
        <h1>
            <a href="http://mono.servicestack.net">ServiceStack</a><b>/</b><a class="active" href="./">The
                Rest Files</a></h1>
    </div>
    <div id="info">
        <div id="net">
            <strong>.NET Clients can access the File Service API with:</strong> <a href="#!files/tests/AsyncRestClientTests.cs.txt">
                Async C# client examples</a> <a href="#!files/tests/SyncRestClientTests.cs.txt">Sync
                    C# client examples</a>
        </div>
        <h3>
            GitHub-like browser with complete remote file management over REST in <a href="https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/RestFiles/RestFiles/default.htm">
                1 page jQuery</a> + <a href="https://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/RestFiles/RestFiles.ServiceInterface/FilesService.cs">
                    1 page C#</a>
        </h3>
        <div id="doc">
            <h4>
                <a href="./metadata">The REST /files web service API:</a></h4>
            <dl>
                <dt><b>GET</b> <a href="./files/dtos/Types">/path/to/folder</a></dt>
                <dd>
                    Directory listing at server</dd>
                <dt><b>GET</b> <a href="./files/README.md">/path/to/file.txt</a></dt>
                <dd>
                    Info and contents of text file at path</dd>
                <dt><b>POST</b> /path/to/newfolder (no-body)</dt>
                <dd>
                    Create a directory at newfolder path</dd>
                <dt><b>POST</b> /path/to/folder (multipart/form-data)</dt>
                <dd>
                    Upload file at folder path</dd>
                <dt><b>PUT</b> /path/to/file.txt</dt>
                <dd>
                    Replace contents of text file at path</dd>
                <dt><b>DELETE</b> /path/to/folder</dt>
                <dd>
                    Delete folder at path</dd>
            </dl>
        </div>
    </div>
    <a id="btn-reset" href="#!revertfiles" class="btn"><span>revert files</span></a>
    <div id="breadcrumb">
    </div>
    <div id="ls">
    </div>
    <div id="fileadmin">
        <div class="mkdir ib">
            <input id="dirname" placeholder="new folder name" /><a class="btn"><span>create folder</span></a></div>
        <div class="upload ib">
            <input type="file" name="fileupload" id="fileupload" /><a class="btn"><span>upload here</span></a></div>
    </div>
    <div id="fileview">
    </div>
    <div id="footer">
        <a id="logo" href="http://mono.servicestack.net">
            <img src="Content/Images/logo-txt-small.png" alt="ServiceStack" /></a> <a id="mono" href="http://www.mono-project.com">
                <img src="Content/Images/Mono-powered-big.png" /></a>
        <div id="demo">
            Live demo hosted on Linux (Cent OS) / <a href="http://wiki.nginx.org">Nginx</a>
            using <a href="http://www.mono-project.com">Mono</a></div>
        <div id="disclaimer">
            *Not affiliated with GitHub, which is a trademark of GitHub Inc.</div>
    </div>
    <div class="bg">
        <div id="choice">
            <a class="close" href="#!closechoice"></a>
            <h3>
                Stop! old, outdated browser detected.</h3>
            <p>
                Unfortunately the advanced techniques used on this page is not supported by your
                current browser.
            </p>
            <p>
                To have a better browser experience for this and other web sites, please choose
                from one of the better alternatives below:
            </p>
            <a id="chrome" href="http://go.microsoft.com/fwlink/?LinkID=166937">
                <img src="Content/Images/Chrome_logo.png" alt="Chrome" /></a> <a id="opera" href="http://go.microsoft.com/fwlink/?LinkID=166934">
                    <img src="Content/Images/Opera_logo.png" alt="Opera" /></a> <a id="firefox" href="http://go.microsoft.com/fwlink/?LinkID=166932">
                        <img src="Content/Images/Mozilla_logo.png" alt="Firefox" /></a> <a id="safari" href="http://go.microsoft.com/fwlink/?LinkID=166939">
                            <img src="Content/Images/Safari_logo.jpg" alt="Safari" /></a>
        </div>
    </div>
    <script>
        if (typeof console == "undefined") { var console = {}; console.log = function () { }; }

        if (($.browser.msie && $.browser.version < 9) || location.href.indexOf("fakeie") >= 0) {
            document.body.className = "lamebrowser";
        }
        $.ajaxSetup({ cache: false, dataType: 'json' });

        function fromDtoDate(dateStr) {
            return new Date(parseFloat(/Date\(([^)]+)\)/.exec(dateStr)[1]));
        }
        function toTwitterTime(a) {
            var b = new Date();
            var c = typeof a == "date" ? a : new Date(a);
            var d = b - c;
            var e = 1000, minute = e * 60, hour = minute * 60, day = hour * 24, week = day * 7;
            if (isNaN(d) || d < 0) { return "" }
            if (d < e * 7) { return "right now" }
            if (d < minute) { return Math.floor(d / e) + " secs ago" }
            if (d < minute * 2) { return "about 1 min ago" }
            if (d < hour) { return Math.floor(d / minute) + " mins ago" }
            if (d < hour * 2) { return "about 1 hour ago" }
            if (d < day) { return Math.floor(d / hour) + " hours ago" }
            if (d > day && d < day * 2) { return "yesterday" }
            if (d < day * 365) { return Math.floor(d / day) + " days ago" } else { return "over a year ago" }
        }
        function enc(html) {
            if (typeof html != "string") return html;
            return html.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
        }
        function dirPath(path) {
            if (typeof path != "string") return path;
            var strPos = path.lastIndexOf('/', path.length - 1);
            if (strPos == -1) return path;
            return path.substr(0, strPos);
        }
        function _mkAjax(url, data, callback, type, method) {
            if ($.isFunction(data)) {
                callback = data, data = {};
            }
            return $.ajax({ type: method, url: url, data: data, success: callback, contentType: type });
        }
        $.extend({
            put: function (url, data, callback, type) {
                return _mkAjax(url, data, callback, type, 'PUT');
            },
            del: function (url, data, callback, type) {
                return _mkAjax(url, data, callback, type, 'DELETE');
            }
        });

        var href = "files", lastHref = "";

        var mkdir = function () {
            var dir = $("#dirname");
            if (!dir.val()) {
                alert("Enter the name of the folder first");
                dir.focus();
                return;
            }
            $.post(href + "/" + dir.val(), null, function () { dir.val(''); refresh(); });
        };
        $(".mkdir .btn").click(mkdir);
        $(".mkdir INPUT").keypress(function (e) { if (e.which == '13') mkdir(); });
        $(".upload .btn").click(function () {
            var file = $("#fileupload").val();
            if (!file) {
                alert("Select a file to upload first");
                return;
            }
            try {
                $.ajaxFileUpload({
                    url: href,
                    secureuri: false,
                    fileElementId: 'fileupload',
                    success: refresh
                });
            }
            catch (e) {
                alert("Not supported in " + ($.browser.msie ? "IE" : "this browser"));
            }
        });
        $(".btn").mousedown(function () { $(this).toggleClass("mousedown"); });
        $(".btn").mouseup(function () { $(this).toggleClass("mousedown"); });

        var refresh = function (callback, skipPushState) {
            if (!skipPushState && window.history.pushState)
                window.history.pushState(href, href.replace('/', ' / '), '#!' + href);

            var dirs = href.replace(/\/$/, "").split('/');
            var sb = '<div id="breadcrumb">';
            var sbDirs = "";
            for (var i = 0; i < dirs.length; i++) {
                var dir = dirs[i];
                if (!dir) continue;
                sb += (i == dirs.length - 1)
                ? '<strong>' + dir + '</strong>'
                : '<a href="#!' + sbDirs + dir + '">' + dir + '</a><b>/</b>';
                sbDirs += dir + "/";
            }
            $("#breadcrumb").html(sb + "</div>");

            var jqLs = $("#ls");
            $.getJSON(href, function (r) {
                var navBack = lastHref.length > href.length && lastHref.substr(0, href.length) == href,
                nextCls = navBack ? "results-0" : "results-2",
                hasResults = $("#ls TABLE").length == 1,
                cls = !hasResults ? "results-1" : nextCls;
                var sb = "<div class='" + cls + "'><table><thead><tr><th>name</th><th>age</th><th>size</th></thead><tbody>";

                var file = r.File;
                if (file) {
                    if (!file.IsTextFile) {
                        location.href = href + "?ForDownload=true";
                        setTimeout(function () { window.history.back() }, 1000);
                        return;
                    }

                    var jqFile = $("#fileview");
                    var sb = "<h3><a class='btn edit' href='#!savechanges'><span>save changes</span></a></dd>"
                       + "<span class='ib txt'></span><dl><dd>" + enc(file.FileSizeBytes) + " bytes</dd><dd>"
                       + toTwitterTime(fromDtoDate(file.ModifiedDate)) + "</dd>"
                       + '<dd><a class="btn download" href="' + href + '?ForDownload=true"><span>download file</span></a></dd>'
                       + "</dl></h3>"
                       + "<textarea>" + enc(file.Contents) + "</textarea>";
                    jqFile.html(sb).show();

                    var height = $("#footer").position().top - $("#ls").position().top;
                    $("#fileview TEXTAREA").height(height - 65);

                    $("#fileadmin").hide();
                    $("#ls").html("").hide();
                    return;
                }

                $("#fileview").html("").hide();
                var dirList = r.Directory;
                if (dirList) {
                    if (dirs.length > 1) {
                        var upHref = href.substr(0, href.lastIndexOf('/', href.length - 2));
                        sb += "<tr><td><a class='up-dir' href='#!" + upHref + "'>..<a></td><td></td><td></td></tr>";
                    }
                    $.each(dirList.Folders, function (i, dir) {
                        sb += "<tr><td><a class='dir' href='#!" + href + "/" + dir.Name + "'>" + dir.Name + "/<a></td><td>"
                        + toTwitterTime(fromDtoDate(dir.ModifiedDate)) + "</td><td>"
                        + dir.FileCount + " files</td>";
                    });
                    $.each(dirList.Files, function (i, file) {
                        sb += "<tr><td><a class='file' href='#!" + href + "/" + file.Name + "'><b class='del' href='#!deletefile'></b>" + file.Name + "</a></td><td>"
                        + toTwitterTime(fromDtoDate(file.ModifiedDate)) + "</td><td>"
                        + file.FileSizeBytes + " bytes</td>";
                    });
                }

                sb += "</tbody></table></div>";

                $("#fileadmin").show();
                $("#ls").show().append(sb);

                var jq1 = $(".results-1"), jq2 = $("." + nextCls), el1 = jq1[0], el2 = jq2[0];
                if (el1 && el2) {
                    jqLs.css({ "min-height": Math.max(jq1.height(), jq2.height()) + "px" });

                    jq1.addClass(navBack ? "slide-right" : "slide-left");
                    jq2.addClass(navBack ? "slide-right" : "slide-left");

                    setTimeout(function () {
                        jqLs.children().first().remove();
                        jqLs.children().first()[0].className = "results-1";
                    }, 450);
                }
                else {
                    $("#ls").css({ "min-height": jq1.height() + "px" });
                }
            });
        }

        window.onpopstate = function (e) {
            e = e || event;
            if (!e.state) return;
            href = e.state;
            refresh(null, true);
        };

        var clickHandlers = {
            files: function (el, e, href) {
                if (e.ctrlKey || e.shiftKey) {
                    window.open('#!' + href);
                    return;
                }
                refresh();
            },
            savechanges: function () {
                $.put(lastHref, { TextContents: $("#fileview textarea").val() }, refresh, 'application/x-www-form-urlencoded');
                href = dirPath(lastHref), location.hash = "#!" + href;
            },
            deletefile: function (el) {
                var fileHref = $(el.parentNode).attr('href').substr(2);
                $.del(fileHref, refresh);
                href = dirPath(fileHref), location.hash = "#!" + href;
            },
            revertfiles: function () {
                if (!confirm("Are you sure you want to revert the filesystem back to its original state?")) return;
                href = "files", location.hash = "#!files";
                $.post("revertfiles", null, refresh);
                return;
            },
            closechoice: function () {
                document.body.className = "";
                href = "files", location.hash = "#!files";
                refresh();
            }
        }

        $(document).click(function (e) {
            var attrHref, el = e.target;
            do { attrHref = el.getAttribute("href"); } while (!attrHref && (el = el.parentElement));
            if (!attrHref) return;

            if (attrHref.substr(0, 2) === "#!") {
                lastHref = href, href = attrHref.substr(2);
                var cmd = href.split('/')[0];

                var clickHandler = clickHandlers[cmd];
                if (clickHandler) {
                    if (e.preventDefault) e.preventDefault();
                    clickHandler(el, e, href);
                }
            }
        });

        var hash = location.hash.indexOf('#!') === 0 && location.hash.substr(2);
        if (hash) href = hash;
        refresh();

    </script>
    <!-- change the UA-XXXXX-X to be your site's ID -->
    <script>
        var _gaq = [['_setAccount', 'UA-7722718-7'], ['_trackPageview']];
        (function (d, t) {
            var g = d.createElement(t),
        s = d.getElementsByTagName(t)[0];
            g.async = true;
            g.src = ('https:' == location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            s.parentNode.insertBefore(g, s);
        })(document, 'script');
    </script>
</body>
</html>
